use std::fs;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use tokio::process::Command;

fn is_executable(metadata: &fs::Metadata) -> bool {
    #[cfg(unix)]
    {
        metadata.permissions().mode() & 0o111 != 0
    }
    #[cfg(not(unix))]
    {
        metadata.file_type().is_file()
    }
}

fn check_path_candidate(path: &Path) -> bool {
    match fs::metadata(path) {
        Ok(metadata) => metadata.file_type().is_file() && is_executable(&metadata),
        Err(_) => false,
    }
}

pub fn command_exists(command: &str) -> bool {
    if command.trim().is_empty() {
        return false;
    }

    let has_separator = command.contains('/') || command.contains('\\');
    if has_separator {
        return check_path_candidate(Path::new(command));
    }

    let path_var = match std::env::var_os("PATH") {
        Some(value) => value,
        None => return false,
    };

    for path in std::env::split_paths(&path_var) {
        if path.as_os_str().is_empty() {
            continue;
        }
        let candidate = path.join(command);
        if check_path_candidate(&candidate) {
            return true;
        }
    }

    false
}

pub fn build_sensevoice_transcribe_command(
    command: &str,
    recording_path: &str,
    models_path: Option<&str>,
    output_path: Option<&str>,
) -> Command {
    let mut cmd = Command::new(command);

    let normalized_models_path = models_path
        .map(|path| path.trim())
        .filter(|path| !path.is_empty());

    if let Some(path) = normalized_models_path {
        cmd.env("MODELS_PATH", path);
        cmd.arg("--models-path").arg(path);
    }

    if let Some(path) = output_path
        .map(|value| value.trim())
        .filter(|value| !value.is_empty())
    {
        cmd.arg("-o").arg(path);
    }

    cmd.arg("--channels=2");
    cmd.arg(recording_path);
    cmd
}

pub fn build_sensevoice_download_command(
    command: &str,
    models_path: &str,
    hf_endpoint: Option<&str>,
) -> Command {
    let mut cmd = Command::new(command);

    let trimmed_models_path = models_path.trim();
    if !trimmed_models_path.is_empty() {
        cmd.env("MODELS_PATH", trimmed_models_path);
        cmd.arg("--models-path").arg(trimmed_models_path);
    }

    if let Some(endpoint) = hf_endpoint
        .map(|value| value.trim())
        .filter(|value| !value.is_empty())
    {
        cmd.env("HF_ENDPOINT", endpoint);
        cmd.env("HF_MIRROR", endpoint);
        cmd.arg("--hf-endpoint").arg(endpoint);
    }

    cmd.arg("--download-only");
    cmd
}

pub fn model_file_path(base_dir: &str) -> PathBuf {
    Path::new(base_dir).join("model.int8.onnx")
}
